#pragma once

#include "vertex_buffer.h"
#include "triangle_renderer_software.h"
#include "renderer.h"

namespace krryn{
namespace scene{
	template
	<
		typename _Vertex
	>
	class vertex_buffer<system::software_tag, _Vertex> : public hardware_buffer{
	public:
		typedef _Vertex vertex_type;
		typedef typename _Vertex::description_type description_type;
		typedef krryn::core::refc_ptr<vertex_buffer> ptr;
		typedef index_buffer<system::software_tag> index_buffer_type;

		vertex_buffer(system::renderer<system::software_tag> &a_Renderer, size_t a_Size) 
			: m_Size(a_Size), m_Renderer(a_Renderer){
			// allocate a fixed size vector for performance.
			// this also guarantees us that the m_Current iterator will never
			// invalid as long as we don't have to resize the vector 
			// (which we don't because operator += does bounds checking).
			m_Vertices = vertex_list_ptr(new vertex_list_type(a_Size));
			m_Current = m_Vertices->begin();
		}

		void lock(lock_type a_Type){
			hardware_buffer::lock(a_Type);
		}

		void unlock(){
			hardware_buffer::unlock();
		}

		void draw(){
			setup_for_drawing();
		}

		void draw_indexed(const index_buffer_type &a_Buffer){
			setup_for_drawing();

			//math::matrix l_Perspective = math::matrix::create_perspective_projection(
			//	//1.f, 1000.f, 1, 0.75);
			//	1.f, 1000.f, m_Renderer.get_canvas()->get_width(), m_Renderer.get_canvas()->get_height());

			// tramsform form clip-space to screen space in a blunt-axe type of way
			math::matrix l_Perspective;
			l_Perspective[math::matrix::D0] = (m_Renderer.get_canvas()->get_width() / 2.f);
			l_Perspective[math::matrix::TX] = (m_Renderer.get_canvas()->get_width() / 2.f);

			l_Perspective[math::matrix::D1] = -(m_Renderer.get_canvas()->get_height() / 2.f);
			l_Perspective[math::matrix::TY] = (m_Renderer.get_canvas()->get_height() / 2.f) - 1;

			typedef typename index_buffer_type::const_iterator iter;

			for(iter i = a_Buffer.begin(); i != a_Buffer.end(); i += 3){
				_Vertex v1 = (*m_Vertices)[i[0]];
				_Vertex v2 = (*m_Vertices)[i[1]];
				_Vertex v3 = (*m_Vertices)[i[2]];

				v1.position(l_Perspective.transform(v1.get_position()));
				v2.position(l_Perspective.transform(v2.get_position()));
				v3.position(l_Perspective.transform(v3.get_position()));

				m_TriangleRenderer.draw(v1, v2, v3);
			}
		}

		void operator +=(_Vertex &a_Vertex){
			if(locked() ^ write){
				throw scene_exception("Buffer must be write-locked for insertion");
			}

			if(m_Vertices->size() > m_Size || m_Current == m_Vertices->end()){
				throw scene_exception("Buffer out of bounds");
			}

			*m_Current++ = a_Vertex;
		}

	private:
		typedef std::vector<_Vertex> vertex_list_type;
		typedef std::auto_ptr<vertex_list_type> vertex_list_ptr;

		typename vertex_list_type::iterator m_Current;

		vertex_list_ptr m_Vertices;
		size_t m_Size;

		system::renderer<system::software_tag> &m_Renderer;
		system_imp::triangle_renderer_software<_Vertex> m_TriangleRenderer;

		void setup_for_drawing(){
			m_TriangleRenderer.set_canvas(m_Renderer.get_canvas());
		}
	};
}
}
